package com.sz.biz.common.base.service.impl;

import com.sz.biz.common.constants.ErrorCodes;
import com.sz.biz.common.constants.ModuleNames;
import com.sz.biz.common.base.entity.ExchangeRate;
import com.sz.biz.common.base.service.ExchangeRateService;
import com.sz.biz.common.base.dto.CurrencyDto;
import com.sz.biz.common.base.dto.ExchangeRateDto;
import com.sz.common.base.constants.UserActions;
import com.sz.common.base.dao.DaoSupport;
import com.sz.common.base.dao.ParamData;
import com.sz.common.base.dto.QResultDto;
import com.sz.common.base.dto.QueryParams;
import com.sz.common.base.exception.Exceptions;
import com.sz.common.base.service.AbstractService;
import com.sz.common.base.utils.DateUtils;
import com.sz.common.core.service.PrincipalLogUtils;
import org.apache.commons.collections.map.HashedMap;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by xutao on 16/11/21.
 */
@Service
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class ExchangeRateServiceImpl extends AbstractService implements ExchangeRateService {
    private static final int ONE_MIN = 1000;
    @Resource(name = "daoSupport")
    private DaoSupport dao;

//    @Autowired
//    BackupExchangeRateService backupExchangeRateService;

    /**
     * 处理左交叉--》属于单向交集
     *
     * @param listCurrnencyRoe:汇率列表
     * @param addedExchangeRate:增加的汇率信息
     * @param curIndex:处理到的汇率当前下标
     * @param addedBeginTime:被增加的汇率开始时间，此处为Date类型
     * @param curRecBeginTime:当前记录的开始时间，此处为Date类型
     * @param confirmAdd:是否确认增加                   true:增加 false:不增加
     * @return 无
     * @throws Exception
     */
    public void leftCrossProc(List<ExchangeRate> listCurrnencyRoe, ExchangeRate addedExchangeRate, int curIndex,
                              Date addedBeginTime, Date curRecBeginTime, boolean confirmAdd) {
        //边界处理
        if (addedBeginTime.compareTo(curRecBeginTime) == 0) {

            if (confirmAdd) {

                ExchangeRate tempRecd = new ExchangeRate();
                BeanUtils.copyProperties(listCurrnencyRoe.get(curIndex), tempRecd);

                //逻辑删除

                tempRecd.setDelete(true);

                dao.update(getSqlName("updateByExchangeId"), tempRecd);
            } else {
                throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHANGERATE_ADD_AFFECT_OTHERDATA);
            }


        } else {

            Date tempDate = new Date(addedExchangeRate.getBeginTime().getTime() - 1000);

            ExchangeRate tempCurRecd = new ExchangeRate();

            BeanUtils.copyProperties(listCurrnencyRoe.get(curIndex), tempCurRecd);

            tempCurRecd.setEndTime(tempDate);
            tempCurRecd.setBeginTime(null);
            tempCurRecd.setRate(null);
            tempCurRecd.setDelete(null);

            if (confirmAdd) {
                dao.update(getSqlName("updateByExchangeId"), tempCurRecd);
            } else {
                throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHANGERATE_ADD_AFFECT_OTHERDATA);
            }
        }

        PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.ADD,
                "新增汇率");
        dao.save(getSqlName("insert"), addedExchangeRate);
    }

    /**
     * 处理右交叉--》属于单向交集
     *
     * @param listCurrnencyRoe:汇率列表
     * @param addedExchangeRate:增加的汇率信息
     * @param curIndex:处理到的汇率当前下标
     * @param addedEndtime:被增加的汇率结束时间，此处为Date类型
     * @param curRecEndTime:当前记录的结束时间，此处为Date类型
     * @param confirmAdd:是否确认增加                 true:增加 false:不增加
     * @return 无
     * @throws Exception
     */
    public void rightCrossProc(List<ExchangeRate> listCurrnencyRoe, ExchangeRate addedExchangeRate, int curIndex,
                               Date addedEndtime, Date curRecEndTime, boolean confirmAdd) {
        //边界处理
        if (addedEndtime.compareTo(curRecEndTime) == 0) {

            ExchangeRate tempRecd = new ExchangeRate();

            BeanUtils.copyProperties(listCurrnencyRoe.get(curIndex), tempRecd);
            //逻辑删除
            tempRecd.setDelete(true);

            if (confirmAdd) {
                dao.update(getSqlName("updateByExchangeId"), tempRecd);
            } else {
                throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHANGERATE_ADD_AFFECT_OTHERDATA);
            }

        } else {
            Date tempDate = new Date(addedExchangeRate.getEndTime().getTime() + 1000);


            ExchangeRate tempCurRecd = new ExchangeRate();

            BeanUtils.copyProperties(listCurrnencyRoe.get(curIndex), tempCurRecd);

            tempCurRecd.setBeginTime(tempDate);
            tempCurRecd.setEndTime(null);
            tempCurRecd.setRate(null);
            tempCurRecd.setDelete(null);

            if (confirmAdd) {
                dao.update(getSqlName("updateByExchangeId"), tempCurRecd);
            } else {
                throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHANGERATE_ADD_AFFECT_OTHERDATA);
            }
        }

        if (curIndex == listCurrnencyRoe.size() - 1) {
//            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.ADD,
//                    "新增汇率");
            dao.save(getSqlName("insert"), addedExchangeRate);
        }
    }

    /**
     * 关联保存，影响到其他的汇率，需要在保存之前先对受影响的记录进行修改
     *
     * @param addedExchangeRate:增加的汇率信息
     * @param curRecd:当前记录
     * @param confirmAdd:是否确认增加         true:增加 false:不增加
     * @return 无
     * @throws Exception
     */
    public void relatedSave(ExchangeRate addedExchangeRate, ExchangeRate curRecd, boolean confirmAdd) {
        if (confirmAdd) {


            dao.update(getSqlName("updateByExchangeId"), curRecd);

//            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.ADD,
//                    "新增汇率");

            dao.save(getSqlName("insert"), addedExchangeRate);

        } else {
            throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHANGERATE_ADD_AFFECT_OTHERDATA);
        }
    }

    /**
     * 左对齐右交叉处理--》属于历史汇率包含被增汇率
     *
     * @param listCurrnencyRoe:汇率列表
     * @param addedExchangeRate:被增汇率信息
     * @param curIndex:当前处理到的汇率下标
     * @param confirmAdd:confirmAdd:是否确认增加 true:增加 false:不增加
     * @return 无
     * @throws
     */
    public void leftAlignRightCrossProc(List<ExchangeRate> listCurrnencyRoe, ExchangeRate addedExchangeRate,
                                        int curIndex, boolean confirmAdd) {
        //先修改历史汇率的开始时间
        Date tempDate = new Date(addedExchangeRate.getEndTime().getTime() + 1000);


        ExchangeRate tempCurRecd = new ExchangeRate();

        BeanUtils.copyProperties(listCurrnencyRoe.get(curIndex), tempCurRecd);

        tempCurRecd.setBeginTime(tempDate);
        tempCurRecd.setEndTime(null);
        tempCurRecd.setRate(null);
        tempCurRecd.setDelete(null);

        //当前情况下需要将结束时间卡到2100年，并将当前记录删除掉
        if (curIndex == 0) {
            addedExchangeRate.setEndTime(listCurrnencyRoe.get(curIndex).getEndTime());
            tempCurRecd.setDelete(true);
        }

        relatedSave(addedExchangeRate, tempCurRecd, confirmAdd);
    }

    /**
     * 左交叉右对齐处理--》属于历史汇率包含被增汇率
     *
     * @param listCurrnencyRoe:汇率列表
     * @param addedExchangeRate:倍增汇率信息
     * @param curIndex:当前处理到的汇率下标
     * @param confirmAdd:是否确认增加        true:增加 false:不增加
     * @return 无
     * @throws
     */
    public void leftCrossRightAlign(List<ExchangeRate> listCurrnencyRoe, ExchangeRate addedExchangeRate,
                                    int curIndex, boolean confirmAdd) {
        Date tempDate = new Date(addedExchangeRate.getBeginTime().getTime() - 1000);

        ExchangeRate tempCurRecd = new ExchangeRate();

        BeanUtils.copyProperties(listCurrnencyRoe.get(curIndex), tempCurRecd);

        tempCurRecd.setEndTime(tempDate);
        tempCurRecd.setBeginTime(null);
        tempCurRecd.setRate(null);
        tempCurRecd.setDelete(null);

        relatedSave(addedExchangeRate, tempCurRecd, confirmAdd);
    }

    /**
     * 左右对齐处理--》属于历史汇率包含被增汇率
     *
     * @param listCurrnencyRoe:汇率列表
     * @param addedExchangeRate:倍增汇率信息
     * @param curIndex:当前处理到的汇率下标
     * @param confirmAdd:是否确认增加        true:增加 false:不增加
     * @return 无
     * @throws
     */
    public void fullAlignProc(List<ExchangeRate> listCurrnencyRoe, ExchangeRate addedExchangeRate,
                              int curIndex, boolean confirmAdd) {
        ExchangeRate tempCurRecd = new ExchangeRate();

        BeanUtils.copyProperties(listCurrnencyRoe.get(curIndex), tempCurRecd);
        //逻辑删除
        tempCurRecd.setDelete(true);

        relatedSave(addedExchangeRate, tempCurRecd, confirmAdd);
    }

    /**
     * 左右交叉处理--》属于历史汇率包含被增汇率
     *
     * @param listCurrnencyRoe:汇率列表
     * @param addedExchangeRate:倍增汇率信息
     * @param curIndex:当前处理到的汇率下标
     * @param confirmAdd:是否确认增加        true:增加 false:不增加
     * @return 无
     * @throws
     */
    public void fullCrossProc(List<ExchangeRate> listCurrnencyRoe, ExchangeRate addedExchangeRate,
                              int curIndex, boolean confirmAdd) {
        //首先插入前半截记录
        ExchangeRate headRecd = new ExchangeRate();
        BeanUtils.copyProperties(listCurrnencyRoe.get(curIndex), headRecd);

        Date tempDate = new Date(addedExchangeRate.getBeginTime().getTime() - 1000);

        headRecd.setEndTime(tempDate);

        if (confirmAdd) {
            if (curIndex == 0) {
                dao.update(getSqlName("updateByExchangeId"), headRecd);
                addedExchangeRate.setEndTime(listCurrnencyRoe.get(curIndex).getEndTime());
            } else {
                dao.save(getSqlName("insert"), headRecd);
                //修改历史汇率,主要修改开始时间
                ExchangeRate hisRecd = new ExchangeRate();
                BeanUtils.copyProperties(listCurrnencyRoe.get(curIndex), hisRecd);

                tempDate = new Date(addedExchangeRate.getEndTime().getTime() + 1000);

                hisRecd.setBeginTime(tempDate);

                hisRecd.setRate(null);
                hisRecd.setEndTime(null);
                hisRecd.setDelete(null);


                dao.update(getSqlName("updateByExchangeId"), hisRecd);
            }


            //插入新增汇率
//            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.ADD,
//                    "新增汇率");

            dao.save(getSqlName("insert"), addedExchangeRate);

        } else {

            throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHANGERATE_ADD_AFFECT_OTHERDATA);
        }
    }

    /**
     * 时间间隔处理，包括左右两端的接续与间隔
     *
     * @param listCurrnencyRoe:汇率列表
     * @param curRecBeginTime:当前记录的开始时间
     * @param curRecEndTime:当前记录的结束时间
     * @param confirmAdd:是否确认增加         true:增加 false:不增加
     * @return 无
     * @throws
     */
    public void timeIntervalProc(List<ExchangeRate> listCurrnencyRoe, ExchangeRate addExchangeRate, Date curRecBeginTime,
                                 Date curRecEndTime, boolean confirmAdd) {
        //判断条件为新增汇率开始时间是否比当前记录的结束时间多一秒
        long timediff1 = addExchangeRate.getBeginTime().getTime() - curRecEndTime.getTime();
        long timediff2 = curRecBeginTime.getTime() - addExchangeRate.getEndTime().getTime();

        /*时间接续*/
        if (timediff1 == ONE_MIN || timediff2 == ONE_MIN) {

            //时间接续，直接插入
//            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.ADD,
//                    "新增汇率");

            dao.save(getSqlName("insert"), addExchangeRate);

        } else {
            /*时间间隔*/
            Date tempDate = null;

            //接续时间，插入记录
            if (addExchangeRate.getBeginTime().after(listCurrnencyRoe.get(0).getEndTime())) {
                tempDate = new Date(curRecEndTime.getTime() + 1000);
                addExchangeRate.setBeginTime(tempDate);
            } else {
                tempDate = new Date(curRecBeginTime.getTime() - 1000);
                addExchangeRate.setEndTime(tempDate);
            }

            if (confirmAdd) {
//                PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.ADD,
//                        "新增汇率");
                dao.save(getSqlName("insert"), addExchangeRate);
            } else {
                throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHAGERATE_AUTO_CONTINUOUS_TIME);
            }
        }
    }


    /**
     * 新增汇率，需要参考的条件如下：
     * 1，与历史汇率存在交集关系，修改历史汇率的结束时间点，将其修改为新增汇率开始前一天的最后一刻钟
     * 2，与历史汇率存在覆盖关系，则删除被覆盖的历史汇率
     * 3，与最新汇率之间离散不接续，则修改最新汇率时间点为新增汇率的前一天最后一刻钟，随后新增汇率
     *
     * @param exchangeRateDto 新增的汇率信息
     * @return 返回0则修改成功，1136代表输入的时间有问题，比如新增汇率的时间点被历史汇率的时间点包含在内
     * @throws Exception
     */
    @Override
    public int addCurrencyRoe(ExchangeRateDto exchangeRateDto) {

        ExchangeRate exchangeRate = new ExchangeRate();
        BeanUtils.copyProperties(exchangeRateDto, exchangeRate);
        exchangeRate.setDelete(false);
        boolean confirmAdd = exchangeRateDto.isConfirm();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //币种之间历史汇率列表，按照开始时间降序排列
        List<ExchangeRate> listCurrnencyRoe = dao.findForList(getSqlName("findByCode"), exchangeRate, ExchangeRate.class);

        //存在币种之间的历史汇率
        if (listCurrnencyRoe.size() != 0) {

            //向前遍历历史汇率，看是否存在覆盖交集等情况
            for (int i = 0; i < listCurrnencyRoe.size(); i++) {
                Date curRecBeginTime = listCurrnencyRoe.get(i).getBeginTime();
                Date curRecEndTime = listCurrnencyRoe.get(i).getEndTime();
                Date addedBeginTime = exchangeRate.getBeginTime();
                Date addedEndtime = exchangeRate.getEndTime();

                boolean isAddedBeginTimeRes = DateUtils.isInDate(addedBeginTime, curRecBeginTime, curRecEndTime);
                boolean isAddedEndTimeRes = DateUtils.isInDate(addedEndtime, curRecBeginTime, curRecEndTime);

                /*插入汇率开始时间交集，需要修改和新增*/
                if (isAddedBeginTimeRes || isAddedEndTimeRes) {

                    /*左交叉*/
                    if (isAddedBeginTimeRes && !isAddedEndTimeRes) {
                        leftCrossProc(listCurrnencyRoe, exchangeRate, i, addedBeginTime, curRecBeginTime, confirmAdd);
                        break;
                    }

                    /*右交叉*/
                    if (!isAddedBeginTimeRes && isAddedEndTimeRes) {
                        rightCrossProc(listCurrnencyRoe, exchangeRate, i, addedEndtime, curRecEndTime, confirmAdd);
                        continue;
                    }

                    /*左右同时交叉*/
                    if (isAddedBeginTimeRes && isAddedEndTimeRes) {

                        /*左对齐，右交叉*/
                        if ((addedBeginTime.compareTo(curRecBeginTime) == 0)
                                && !(addedEndtime.compareTo(curRecEndTime) == 0)) {
                            leftAlignRightCrossProc(listCurrnencyRoe, exchangeRate, i, confirmAdd);
                            break;
                        }

                        /*左交叉，右对齐*/
                        if (!(addedBeginTime.compareTo(curRecBeginTime) == 0)
                                && (addedEndtime.compareTo(curRecEndTime) == 0)) {
                            leftCrossRightAlign(listCurrnencyRoe, exchangeRate, i, confirmAdd);
                            break;
                        }

                        /*全对齐*/
                        if ((addedBeginTime.compareTo(curRecBeginTime) == 0)
                                && (addedEndtime.compareTo(curRecEndTime) == 0)) {
                            fullAlignProc(listCurrnencyRoe, exchangeRate, i, confirmAdd);
                            break;
                        }

                        /*全交叉*/
                        if (!(addedBeginTime.compareTo(curRecBeginTime) == 0)
                                && !(addedEndtime.compareTo(curRecEndTime) == 0)) {
                            fullCrossProc(listCurrnencyRoe, exchangeRate, i, confirmAdd);
                            break;
                        }
                    }
                } else {
                    Date listHeadEndTime = listCurrnencyRoe.get(0).getEndTime();
                    Date listTailBeginTime = listCurrnencyRoe.get(listCurrnencyRoe.size() - 1).getBeginTime();

                    /*处理时间间隔，包括连续时间*/
                    if (exchangeRate.getBeginTime().after(listHeadEndTime)
                            || (exchangeRate.getEndTime().before(listTailBeginTime) && (i == listCurrnencyRoe.size() - 1))
                            ) {
                        timeIntervalProc(listCurrnencyRoe, exchangeRate, curRecBeginTime, curRecEndTime, confirmAdd);
                        break;
                    }

                    /*处理时间覆盖，进行逻辑删除*/
                    if ((DateUtils.isInDate(curRecBeginTime, exchangeRate.getBeginTime(), exchangeRate.getEndTime()))
                            && (DateUtils.isInDate(curRecEndTime, exchangeRate.getBeginTime(), exchangeRate.getEndTime()))
                            ) {
                        //逻辑删除
                        ExchangeRate tempRecd = new ExchangeRate();
                        BeanUtils.copyProperties(listCurrnencyRoe.get(i), tempRecd);
                        tempRecd.setDelete(true);

                        if (confirmAdd) {
                            dao.update(getSqlName("updateByExchangeId"), tempRecd);
                        } else {
                            throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHANGERATE_ADD_AFFECT_OTHERDATA);
                        }

                        //此时需要判断临界条件，如果历史记录全被覆盖，则在最后一条被删除时新增汇率
                        if (i == listCurrnencyRoe.size() - 1) {
//                            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.ADD,
//                                    "新增汇率");
                            dao.save(getSqlName("insert"), exchangeRate);
                            break;
                        }
                        continue;
                    }
                }
            }
        } else {

            //如果是第一条记录，则直接插入
//            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.ADD,
//                    "新增汇率");

            Date tempDate = null;
            try {
                tempDate = sdf.parse("2100-01-01 00:00:00");
            } catch (ParseException e) {
                logger.error("新增的汇率异常",e);
            }

            exchangeRate.setEndTime(tempDate);

            dao.save(getSqlName("insert"), exchangeRate);
        }
        //保存操作日志
        Map<String, Object> dataBackup = new HashMap<>();
        dataBackup.put("ADD_EXCHANGE_RATE", exchangeRate);
        PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.ADD, "新建汇率", dataBackup);
//        backupData(exchangeRate);
        return 0;
    }

    /**
     * 逻辑删除
     *
     * @param listCurrnencyRoe:汇率列表
     * @param curIndex:当前记录的下标
     * @param confirmUpdate:是否确认修改  true:修改 false:不不修改
     * @return 无
     * @throws
     */
    public void logicDelete(List<ExchangeRate> listCurrnencyRoe, boolean confirmUpdate, int curIndex) {
        if (confirmUpdate) {

            ExchangeRate tempRecd = new ExchangeRate();

            BeanUtils.copyProperties(listCurrnencyRoe.get(curIndex), tempRecd);

            tempRecd.setDelete(true);

            //保存操作日志
            Map<String, Object> dataBackup = new HashMap<>();
            dataBackup.put("DELETE_EXCHANGE_RATE", tempRecd.getId());
            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.DELETE, "删除汇率", dataBackup);
            dao.update(getSqlName("updateByExchangeId"), tempRecd);
        } else {
            throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHANGERATE_UPDATE_AFFECT_OTHERDATA);
        }
    }

    /**
     * 从被修改的汇率向前查找被影响到的汇率并处理
     *
     * @param moddifiedIndex:被修改汇率在汇率列表中的索引编号
     * @param listCurrnencyRoe:汇率列表
     * @param modifiedEndTime:修改后汇率的结束时间
     * @param confirmUpdate:是否确认修改            true:修改 false:不不修改
     * @return 无
     * @throws
     */
    public void preTraverseProc(int moddifiedIndex, List<ExchangeRate> listCurrnencyRoe,
                                Date modifiedEndTime, boolean confirmUpdate) {
        //从修改的记录向最新汇率遍历，处理各种情况
        for (int i = moddifiedIndex - 1; i >= 0; i--) {

            Date curRecdBeginTime = listCurrnencyRoe.get(i).getBeginTime();
            Date curRecdEndTime = listCurrnencyRoe.get(i).getEndTime();

            boolean isInCurDate = DateUtils.isInDate(modifiedEndTime, curRecdBeginTime, curRecdEndTime);

            if ((!isInCurDate && modifiedEndTime.before(curRecdBeginTime))
                    || (isInCurDate && (modifiedEndTime.compareTo(curRecdEndTime) != 0))) {
                //直接修改当前汇率的开始时间为被修改时间的下一秒

                Date tempDate = new Date(modifiedEndTime.getTime() + 1000);

                ExchangeRate tempRecd = new ExchangeRate();

                BeanUtils.copyProperties(listCurrnencyRoe.get(i), tempRecd);

                tempRecd.setRate(null);
                tempRecd.setEndTime(null);
                tempRecd.setDelete(null);

                tempRecd.setBeginTime(tempDate);

                if (confirmUpdate) {
                    dao.update(getSqlName("updateByExchangeId"), tempRecd);
                } else {
                    throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHANGERATE_UPDATE_AFFECT_OTHERDATA);
                }

                break;
            }

            if ((modifiedEndTime.after(curRecdEndTime))
                    || (modifiedEndTime.compareTo(curRecdEndTime) == 0)) {

                //直接删除,逻辑删除

                logicDelete(listCurrnencyRoe, confirmUpdate, i);

            }
        }
    }

    /**
     * 从被修改的汇率向前查找被影响到的汇率并处理
     *
     * @param moddifiedIndex:被修改汇率在汇率列表中的索引编号
     * @param listCurrnencyRoe:汇率列表
     * @param modifiedBeginTime:修改后汇率的开始时间
     * @param confirmUpdate:是否确认修改            true:修改 false:不不修改
     * @return 无
     * @throws
     */
    public void backTraverseProc(int moddifiedIndex, List<ExchangeRate> listCurrnencyRoe,
                                 Date modifiedBeginTime, boolean confirmUpdate) {
        //从修改的记录向最老的汇率遍历，处理各种情况
        for (int i = moddifiedIndex + 1; i < listCurrnencyRoe.size(); i++) {
            Date curRecdBeginTime = listCurrnencyRoe.get(i).getBeginTime();
            Date curRecdEndTime = listCurrnencyRoe.get(i).getEndTime();

            boolean isInCurDate = DateUtils.isInDate(modifiedBeginTime, curRecdBeginTime, curRecdEndTime);

            if ((!isInCurDate && modifiedBeginTime.after(curRecdEndTime))
                    || (isInCurDate && (modifiedBeginTime.compareTo(curRecdBeginTime) != 0))) {
                //直接修改当前汇率的结束时间为被修改时间的前一秒
                Date tempDate = new Date(modifiedBeginTime.getTime() - 1000);

                ExchangeRate tempRecd = new ExchangeRate();

                BeanUtils.copyProperties(listCurrnencyRoe.get(i), tempRecd);

                tempRecd.setRate(null);
                tempRecd.setBeginTime(null);
                tempRecd.setDelete(null);

                tempRecd.setEndTime(tempDate);

                if (confirmUpdate) {
                    dao.update(getSqlName("updateByExchangeId"), tempRecd);
                } else {
                    throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHANGERATE_UPDATE_AFFECT_OTHERDATA);
                }

                break;
            }

            if ((modifiedBeginTime.before(curRecdBeginTime))
                    || (modifiedBeginTime.compareTo(curRecdBeginTime) == 0)) {

                //直接删除,逻辑删除
                logicDelete(listCurrnencyRoe, confirmUpdate, i);
            }
        }
    }

    /**
     * 通过exchangeid修改对应的汇率
     *
     * @param exchangeRateDto
     * @return
     * @throws Exception
     */
    @Override
    public int updateCurrencyRoe(ExchangeRateDto exchangeRateDto) {

        if (exchangeRateDto != null) {

            ExchangeRate exchangeRate = new ExchangeRate();

            BeanUtils.copyProperties(exchangeRateDto, exchangeRate);

            boolean confirmUpdate = exchangeRateDto.isConfirm();

            //被修改的记录在查询列表中的索引
            int moddifiedIndex = 0;


            ExchangeRate queryParam = (ExchangeRate) dao.findForObject(getSqlName("selectByPrimaryKey"), exchangeRate.getId());

            if (queryParam != null && exchangeRate != null) {

                if (exchangeRate.getBeginTime() != null
                        && queryParam.getEndTime() != null
                        && exchangeRate.getBeginTime().after(queryParam.getEndTime())) {

                    throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHAGERATE_DATA_RULE_ERROR);
                }

                if (exchangeRate.getEndTime() != null
                        && queryParam.getBeginTime() != null
                        && exchangeRate.getEndTime().before(queryParam.getBeginTime())) {

                    throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHAGERATE_DATA_RULE_ERROR);
                }
            }

            if (exchangeRateDto.getBeginTime() != null
                    && exchangeRateDto.getEndTime() != null
                    && exchangeRateDto.getBeginTime().after(exchangeRateDto.getEndTime())) {

                throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHAGERATE_DATA_RULE_ERROR);
            }

            //币种之间历史汇率列表，按照开始时间降序排列
            List<ExchangeRate> listCurrnencyRoe = dao.findForList(getSqlName("findByCode"), queryParam, ExchangeRate.class);

            //最后一条记录的结束时间2100年不能被修改
            if (listCurrnencyRoe.get(0).getId() == exchangeRate.getId()) {
                exchangeRate.setEndTime(listCurrnencyRoe.get(0).getEndTime());
            }

            //如果只是修改汇率，则直接修改完汇率返回
            if ((queryParam != null)
                    && (queryParam.getBeginTime().equals(exchangeRate.getBeginTime()))
                    && (queryParam.getEndTime().equals(exchangeRate.getEndTime()))) {

                if (!queryParam.getRate().equals(exchangeRate.getRate())) {

                    dao.update(getSqlName("updateByExchangeId"), exchangeRate);
                    //保存操作日志
                    Map<String, Object> dataBackup = new HashMap<>();
                    dataBackup.put("UPDATE_EXCHANGE_RATE", exchangeRate);
                    PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.UPDATE, "修改汇率", dataBackup);
//                backupData(exchangeRate);
                    return 0;
                } else {

                    //任何信息都不发生修改，直接返回
                    return 0;
                }

            }


            if (listCurrnencyRoe.size() != 0) {

                Date modifiedBeginTime = exchangeRate.getBeginTime();
                Date modifiedEndTime = exchangeRate.getEndTime();


                //查询被修改记录位于列表中的索引
                for (int i = 0; i < listCurrnencyRoe.size(); i++) {
                    if (exchangeRate.getId() == listCurrnencyRoe.get(i).getId()) {
                        moddifiedIndex = i;
                        break;
                    }
                }

                preTraverseProc(moddifiedIndex, listCurrnencyRoe, modifiedEndTime, confirmUpdate);
                backTraverseProc(moddifiedIndex, listCurrnencyRoe, modifiedBeginTime, confirmUpdate);

                dao.update(getSqlName("updateByExchangeId"), exchangeRate);
                //保存操作日志
                Map<String, Object> dataBackup = new HashMap<>();
                dataBackup.put("UPDATE_EXCHANGE_RATE", exchangeRate);
                PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.UPDATE, "修改汇率", dataBackup);
            }
        }
        return 0;
    }

    /**
     * 删除本币与辅币之间的所有汇率,注意:逻辑删除,本函数批量删除只能支持一个汇率中多个区间的删除
     *
     * @param listId:指定删除的汇率ID列表
     * @return
     * @throws Exception
     */
    @Override
    public int deleteCurrencyRoe(List<Integer> listId) {

        if (listId == null || listId.size() <= 0) {
            throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_EMPTY);
        }

        if (listId.size() > 0) {

            Collections.sort(listId);

            ExchangeRate exchangeRate = (ExchangeRate) dao.findForObject(getSqlName("selectByPrimaryKey"), listId.get(0));

            ExchangeRate tempExchangeRate = new ExchangeRate();
            tempExchangeRate.setSourceCurrency(exchangeRate.getSourceCurrency());
            tempExchangeRate.setDestCurrency(exchangeRate.getDestCurrency());

            List<ExchangeRate> exchangeRateList = dao.findForList(getSqlName("findByCode"), tempExchangeRate, ExchangeRate.class);


            List<Integer> roeList = new ArrayList<Integer>();

            for (int i = 0; i < listId.size(); i++) {
                roeList.add(exchangeRateList.get(i).getId());
            }

            Collections.sort(roeList);

            //String ids = "";

            for (int i = 0; i < listId.size(); i++) {
                if (listId.get(i).intValue() != roeList.get(i).intValue()) {
                    throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_EXCHAGERATE_NOT_LATEST_ROE);
                }

                //ids = ids + listId.get(i) + ",";
            }

//            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.DELETE,
//                    "删除汇率,汇率ID:" + ids);

            //执行批量删除

            dao.delete(getSqlName("deleteByIds"), listId);

            /*删除之后需要将最新的记录结束时间拉到2100年1月1号00:00:00*/
            if (exchangeRateList.size() > listId.size()) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                ExchangeRate curLatestRec = new ExchangeRate();
                BeanUtils.copyProperties(exchangeRateList.get(listId.size()), curLatestRec);

                curLatestRec.setRate(null);
                curLatestRec.setDelete(null);
                curLatestRec.setBeginTime(null);

                Date tempDate = null;
                try {
                    tempDate = sdf.parse("2100-01-01 00:00:00");
                } catch (ParseException e) {
                    logger.error("删除汇率异常",e);
                }

                curLatestRec.setEndTime(tempDate);

                dao.update(getSqlName("updateByExchangeId"), curLatestRec);
            }

//            backupData(exchangeRate);
        }
        return 0;
    }

    /**
     * 删除汇率，由于deleteCurrencyRoe只能进行同一汇率之间的删除，此处进行扩展，在deleteCurrencyRoe的基础
     * 之上对list中的ID进行归类，做到不同汇率都可进行删除
     *
     * @param listIds：
     * @return
     * @throws Exception
     */
    @Override
    public int batchDeleteExchangeRate(List<Integer> listIds) {

        if (listIds != null && listIds.size() > 0) {

            List<ExchangeRate> exchangeRateList = dao.findForList(getSqlName("selectByIds"), listIds, ExchangeRate.class);

            List<Integer> tempIds = new ArrayList<Integer>();

            tempIds.add(exchangeRateList.get(0).getId());

            for (int i = 0; i < exchangeRateList.size() - 1; i++) {

                if (!exchangeRateList.get(i).getSourceCurrency().equalsIgnoreCase(exchangeRateList.get(i + 1).getSourceCurrency())
                        || !exchangeRateList.get(i).getDestCurrency().equalsIgnoreCase(exchangeRateList.get(i + 1).getDestCurrency())) {

                    deleteCurrencyRoe(tempIds);
                    tempIds.clear();
                    tempIds.add(exchangeRateList.get(i + 1).getId());
                    continue;
                }

                tempIds.add(exchangeRateList.get(i + 1).getId());
            }

            deleteCurrencyRoe(tempIds);
        }
        //保存操作日志
        Map<String, Object> dataBackup = new HashMap<>();
        dataBackup.put("DELETE_EXCHANGE_RATE", listIds);
        PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.DELETE, "删除汇率", dataBackup);
        return 0;
    }

    /**
     * 查找本币与辅币之间的所有汇率
     *
     * @param sourceCurrency：本币 destCurrency:辅币 pageIndex：页码 pageSize：页大小
     * @return 返回查找到的所有汇率
     * @throws Exception
     */
    @Override
    public QResultDto findCurrencyRoe(String sourceCurrency, String destCurrency, Date beginTime, Date endTime, String q, int pageIndex, int pageSize,
                                      String sortName, boolean isAsc, String language) {

        QueryParams params = new QueryParams(pageSize, pageIndex, null, sortName, isAsc, null);
        params.put("sourceCurrency", sourceCurrency);
        params.put("destCurrency", destCurrency);
        params.put("beginTime", beginTime);
        params.put("endTime", endTime);
        params.put("q", q);
        params.put("language", language);

        ParamData pd = this.convertQueryParams(params);

        List<ExchangeRate> listExchangerRate = dao.findForList(getSqlName("exchangeRateListPage"), pd, ExchangeRate.class);

        List<ExchangeRateDto> exchangeRateDtoList = translateCode2Name(listExchangerRate, language);

        QResultDto qResultDto = new QResultDto();
        qResultDto.setData(exchangeRateDtoList);
        qResultDto.setPagination(pd.getPagination());

        return qResultDto;
    }

    /**
     * 按时间查找汇率
     *
     * @param sourceCurrency 本币
     * @param destCurrency   辅币
     * @param date           时间
     * @return 返回汇率
     * @throws Exception
     */
    @Override
    public ExchangeRate findCurrencyRoeByDate(String sourceCurrency, String destCurrency, Date date) {
        Map<String,Object> map = new HashMap<>();
        map.put("sourceCurrency",sourceCurrency);
        map.put("destCurrency",destCurrency);
        map.put("date",date);
        return (ExchangeRate)dao.findForObject("findCurrencyRoeByDate",map);
    }

    @Override
    public QResultDto getLatestExchangeRateList(String sourceCurrency, String destCurrency, String q, int pageIndex, int pageSize,
                                                String sortName, boolean isAsc, String language) {

        QueryParams params = new QueryParams(pageSize, pageIndex, null, sortName, isAsc, null);
        params.put("sourceCurrency", sourceCurrency);
        params.put("destCurrency", destCurrency);
        params.put("q", q);
        params.put("language", language);

        ParamData pd = this.convertQueryParams(params);

        List<ExchangeRate> listExchangerRate = dao.findForList(getSqlName("getLatestExchangeListPage"), pd, ExchangeRate.class);

        List<ExchangeRateDto> exchangeRateDtoList = translateCode2Name(listExchangerRate, language);

        QResultDto qResultDto = new QResultDto();
        qResultDto.setData(exchangeRateDtoList);
        qResultDto.setPagination(pd.getPagination());

        return qResultDto;
    }

    public List<ExchangeRateDto> translateCode2Name(List<ExchangeRate> listExchangerRate, String language) {

        List<ExchangeRateDto> exchangeRateDtoList = new ArrayList<ExchangeRateDto>();

        for (int i = 0; i < listExchangerRate.size(); i++) {

            ExchangeRateDto tempExchangeRateDto = new ExchangeRateDto();
            BeanUtils.copyProperties(listExchangerRate.get(i), tempExchangeRateDto);

            Map<String, String> querySourceMap = new HashedMap();
            querySourceMap.put("language", language);
            querySourceMap.put("code", listExchangerRate.get(i).getSourceCurrency());

            List<CurrencyDto> existSourceRecdList = dao.findForList("CurrencyMapper.findCurrencyNameByCode", querySourceMap, CurrencyDto.class);

            if (existSourceRecdList != null && existSourceRecdList.size() > 0) {

                tempExchangeRateDto.setSourceName(existSourceRecdList.get(0).getName());
            }

            Map<String, String> queryDestMap = new HashedMap();
            queryDestMap.put("language", language);
            queryDestMap.put("code", listExchangerRate.get(i).getDestCurrency());

            List<CurrencyDto> existDestRecdList = dao.findForList("CurrencyMapper.findCurrencyNameByCode", queryDestMap, CurrencyDto.class);

            if (existDestRecdList != null && existDestRecdList.size() > 0) {

                tempExchangeRateDto.setDestName(existDestRecdList.get(0).getName());
            }

            exchangeRateDtoList.add(tempExchangeRateDto);
        }
        return exchangeRateDtoList;
    }

    public void backupData(ExchangeRate exchangeRate) {

//        List<ExchangeRate> exchangeRateList = dao.findForList(getSqlName("findByCode"), exchangeRate, ExchangeRate.class);
//
//        if (exchangeRateList != null && exchangeRateList.size() > 0) {
//
//            BackupExchangeRateDto backupExchangeRateDto = new BackupExchangeRateDto();
//            List<BackupSubExchangeRate> backupSubExchangeRateList = new ArrayList<BackupSubExchangeRate>();
//
//            backupExchangeRateDto.setCreator(PrincipalUtils.getAccountName());
//            backupExchangeRateDto.setDestCurrency(exchangeRateList.get(0).getDestCurrency());
//            backupExchangeRateDto.setSourceCurrency(exchangeRateList.get(0).getSourceCurrency());
//
//            for (int i=0; i<exchangeRateList.size(); i++) {
//
//                BackupSubExchangeRate backupSubExchangeRate = new BackupSubExchangeRate();
//                backupSubExchangeRate.setBeginTime(exchangeRateList.get(i).getBeginTime());
//                backupSubExchangeRate.setEndTime(exchangeRateList.get(i).getEndTime());
//                backupSubExchangeRate.setRate(exchangeRateList.get(i).getRate());
//
//                backupSubExchangeRateList.add(backupSubExchangeRate);
//            }
//
//            backupExchangeRateDto.setBackupSubExchangeRateList(backupSubExchangeRateList);
//            backupExchangeRateService.backupExchangeRate(backupExchangeRateDto);
//            //保存操作日志
//            Map<String, Object> dataBackup = new HashMap<>();
//            dataBackup.put("UPDATE_EXCHANGE_RATE", exchangeRate);
//            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_EXCHANGE_RATE, UserActions.UPDATE, "修改汇率", dataBackup);
//        }
    }

    @Override
    protected String getMapperNamespace() {
        return "ExchangeRateMapper";
    }
}
